<?php

declare(strict_types=1);

namespace Haozing\Core\Command;

use Hyperf\CodeParser\Project;
use Hyperf\Command\Annotation\Command;
use Hyperf\Contract\ConfigInterface;
use Hyperf\Command\Command as HyperfCommand;
use Hyperf\Database\Commands\Ast\ModelRewriteConnectionVisitor;
use Hyperf\Database\Commands\Ast\ModelUpdateVisitor;
use Hyperf\Database\Commands\ModelCommand as HyperfModelCommand;
use Hyperf\Database\Commands\ModelData;
use Hyperf\Database\Commands\ModelOption;
use Hyperf\Stringable\Str;
use Hyperf\Support\Composer;
use PhpParser\NodeTraverser;
use PhpParser\NodeVisitor\CloningVisitor;
use Psr\Container\ContainerInterface;
use Symfony\Component\Console\Input\InputArgument;
use Symfony\Component\Console\Input\InputOption;
use function Hyperf\Collection\data_get;
use function Hyperf\Support\make;

#[Command(name: "plugin:service")]
class ServiceCommand  extends HyperfModelCommand
{
    protected $pluginName;
    protected function createModel(string $table, ModelOption $option): void
    {
        //新增plugin名称指令
        $this->pluginName = $this->input->getArgument('name');
        //设置父类
        $option->setUses("Haozing\Core\Service\Model\AbstractModel");
        $option->setInheritance("AbstractModel");
        //设置casts
        $option->setForceCasts(true);
        $option->setRefreshFillable(true);
        //设置注释
        $option->setWithComments(true);
        $option->setPropertyCase(1);

        $builder = $this->getSchemaBuilder($option->getPool());
        $table = Str::replaceFirst($option->getPrefix(), '', $table);
        $columns = $this->formatColumns($builder->getColumnTypeListing($table));

        $class = $option->getTableMapping()[$table] ?? Str::studly(Str::singular($table));
        $path = BASE_PATH . DIRECTORY_SEPARATOR . 'plugins'.DIRECTORY_SEPARATOR.$this->pluginName. DIRECTORY_SEPARATOR . 'src'. DIRECTORY_SEPARATOR . 'Service'. DIRECTORY_SEPARATOR . 'Model'. DIRECTORY_SEPARATOR . $class . '.php';

        $class = "Plugins\\" . Str::studly($this->pluginName). "\\Service\\Model\\". $class;

        if (! file_exists($path)) {
            $this->mkdir($path);
            file_put_contents($path, $this->buildClass($table, $class, $option));
        }

        /**
         * ["column_key"]=>
         * string(0) ""
         * ["column_name"]=>
         * string(10) "ex_user_id"
         * ["data_type"]=>
         * string(3) "int"
         * ["column_comment"]=>
         * string(35) "用户中心企业登录账号的id"
         * ["extra"]=>
         * string(0) ""
         * ["column_type"]=>
         * string(7) "int(11)"
         * **********************************
         * ["column_key"]=>
         * string(3) "PRI"
         * ["column_name"]=>
         * string(2) "id"
         * ["data_type"]=>
         * string(3) "int"
         * ["column_comment"]=>
         * string(0) ""
         * ["extra"]=>
         * string(14) "auto_increment"
         * ["column_type"]=>
         * string(16) "int(10) unsigned"
         */
        $columns = $this->getColumns($class, $columns, $option->isForceCasts());

        $traverser = new NodeTraverser();
        $traverser->addVisitor(make(ModelUpdateVisitor::class, [
            'class' => $class,
            'columns' => $columns,
            'option' => $option,
        ]));
        $traverser->addVisitor(make(ModelRewriteConnectionVisitor::class, [$class, $option->getPool()]));
        $data = make(ModelData::class, ['class' => $class, 'columns' => $columns]);
        foreach ($option->getVisitors() as $visitorClass) {
            $traverser->addVisitor(make($visitorClass, [$option, $data]));
        }

        $traverser->addVisitor(new CloningVisitor());

        $originStmts = $this->astParser->parse(file_get_contents($path));
        $originTokens = $this->lexer->getTokens();
        $newStmts = $traverser->traverse($originStmts);
        $code = $this->printer->printFormatPreserving($newStmts, $originStmts, $originTokens);

        file_put_contents($path, $code);
        $this->output->writeln(sprintf('<info>Model %s was created.</info>', $class));

        if ($option->isWithIde()) {
            $this->generateIDE($code, $option, $data);
        }

        if ($this->input->getOption('gs')){
            $this->createIService($this->pluginName, $table);
            //生成服务
            $this->createService($this->pluginName, $table);
        }
        if ($this->input->getOption('gapi')){

            $this->createApi($this->pluginName, $table,$columns);
        }

        if ($this->input->getOption('ga')){
            $this->createIAction($this->pluginName, $table);
            $this->createAction($this->pluginName, $table,$columns);
        }

        if ($this->input->getOption('gl')){
            $this->createILogic($this->pluginName, $table);
            $this->createLogic($this->pluginName, $table);
        }

    }

    protected function configure(): void
    {
        $this->addArgument('name', InputArgument::OPTIONAL, '插件的名称', 'demo');
        $this->addOption('gs','gs', InputOption::VALUE_NONE, '是否生成服务');
        $this->addOption('gapi','gapi', InputOption::VALUE_NONE, '是否生成api[包含验签]');
        $this->addOption('ga','ga', InputOption::VALUE_NONE, '是否生成action');
        $this->addOption('gl','gl', InputOption::VALUE_NONE, '是否生成logic');
        parent::configure();
    }

    /**
     * Build the class with the given name.
     */
    protected function buildClass(string $table, string $name, ModelOption $option): string
    {
        $stub = file_get_contents(__DIR__ . '/stubs/Model.stub');

        return $this->replaceNamespace($stub, $name)
            ->replaceInheritance($stub, $option->getInheritance())
            ->replaceConnection($stub, $option->getPool())
            ->replaceUses($stub, $option->getUses())
            ->replaceClass($stub, $name)
            ->replaceTable($stub, $table);
    }
    protected function createIService(string $pluginName, string $table): void
    {
        $path = BASE_PATH . DIRECTORY_SEPARATOR . 'plugins'.DIRECTORY_SEPARATOR.$this->pluginName. DIRECTORY_SEPARATOR . 'src'. DIRECTORY_SEPARATOR . 'Service'. DIRECTORY_SEPARATOR .'Interface'. DIRECTORY_SEPARATOR .'I'. Str::studly(Str::singular($table)) . 'Service.php';

        $stub = file_get_contents(__DIR__ . '/stubs/IService.stub');
        $fileContent = str_replace(
            ['%NAME%', '%MODEL%'],
            [Str::studly($pluginName), Str::studly(Str::singular($table))],
            $stub
        );
        if (! file_exists($path)) {
            $this->mkdir($path);
            file_put_contents($path, $fileContent);
        }
    }
    protected function createService(string $pluginName, string $table): void
    {
        $path = BASE_PATH . DIRECTORY_SEPARATOR . 'plugins'.DIRECTORY_SEPARATOR.$this->pluginName. DIRECTORY_SEPARATOR . 'src'. DIRECTORY_SEPARATOR . 'Service'. DIRECTORY_SEPARATOR . Str::studly(Str::singular($table)) . 'Service.php';

        $stub = file_get_contents(__DIR__ . '/stubs/Service.stub');
        $fileContent = str_replace(
            ['%NAME%', '%MODEL%'],
            [Str::studly($pluginName), Str::studly(Str::singular($table))],
            $stub
        );
        if (! file_exists($path)) {
            $this->mkdir($path);
            file_put_contents($path, $fileContent);
        }
    }
    //createApi
    protected function createApi(string $pluginName, string $table,array $columns): void
    {
        $path = BASE_PATH . DIRECTORY_SEPARATOR . 'plugins'.DIRECTORY_SEPARATOR.$this->pluginName. DIRECTORY_SEPARATOR . 'src'. DIRECTORY_SEPARATOR .'Api'. DIRECTORY_SEPARATOR . Str::studly(Str::singular($table)) . 'Api.php';

        $validate = '';
        $validateMsg = '';

        foreach ($columns as $column){
            $columnName = Str::lcfirst(Str::studly(Str::singular($column['column_name'])));
            if ($column['column_key'] != 'PRI' && $column['column_name'] != 'updated_at' && $column['column_name'] != 'deleted_at'){

                $validate .= "                '{$columnName}' => 'required',\n";
                $validateMsg .= "                '{$columnName}.required' => '{$column['column_comment']}不能为空',\n";

            }
        }
        $stub = file_get_contents(__DIR__ . '/stubs/Api.stub');
        $fileContent = str_replace(
            ['%NAME%', '%MODEL%', '%MODELX%','%VALIDATE%','%VALIDATEMSG%','%XNAME%'],
            [Str::studly($pluginName), Str::studly(Str::singular($table)),Str::lcfirst(Str::studly(Str::singular($table))),$validate,$validateMsg,Str::lcfirst(Str::studly($pluginName))],
            $stub
        );
        if (! file_exists($path)) {
            $this->mkdir($path);
            file_put_contents($path, $fileContent);
        }
    }

    //createAction
    protected function createAction(string $pluginName, string $table,array $columns): void
    {
        $path = BASE_PATH. DIRECTORY_SEPARATOR. 'plugins'.DIRECTORY_SEPARATOR.$this->pluginName. DIRECTORY_SEPARATOR.'src'. DIRECTORY_SEPARATOR.'Action'. DIRECTORY_SEPARATOR. Str::studly(Str::singular($table)). 'Action.php';
        $addValidate = '';
        $updateValidate = '';
        $listValidate = '';
        foreach ($columns as $column){
            $columnName = Str::lcfirst(Str::studly(Str::singular($column['column_name'])));
            if ($column['column_key'] != 'PRI' && $column['column_name'] != 'updated_at' && $column['column_name'] != 'deleted_at'){
                //暂时只判断int、字符串、时间
                //判断$column['column_type']字符串是否包含int，如果包含，则添加int验证，判断是否包含time，如果包含，则添加时间验证，否则添加字符串验证
                if (Str::contains($column['column_type'],'int')){
                    $addValidate.= '            \''.$column['column_name'].'\' => $params[\''.$columnName.'\'] ?? 0,'."\n";
                    $updateValidate.= '        if (isset($params[\''.$columnName.'\'])) {'."\n".'            $data[\''.$column['column_name'].'\'] = $params[\''.$columnName.'\'];'."\n".'        }'."\n";
                    $listValidate.= '        if (isset($params[\''.$columnName.'\']) && $params[\''.$columnName.'\']) {'."\n".'            $where[] = [\''.$column['column_name'].'\',$params[\''.$columnName.'\']];'."\n".'        }'."\n";

                    continue;
                }
                if ($column['column_name'] == 'created_at'){
                    $addValidate.= '            \''.$column['column_name'].'\' => date(\'Y-m-d H:i:s\'),'."\n";
                    $updateValidate.= '        if (isset($params[\''.$columnName.'\'])) {'."\n".'            $data[\''.$column['column_name'].'\'] = $params[\''.$columnName.'\'];'."\n".'        }'."\n";
                    $listValidate.= '        if (isset($params[\''.$columnName.'\']) && $params[\''.$columnName.'\']) {'."\n".'            $where[] = [\''.$column['column_name'].'\',$params[\''.$columnName.'\']];'."\n".'        }'."\n";
                    continue;
                }
                $addValidate.= '            \''.$column['column_name'].'\' => $params[\''.$columnName.'\'] ?? \'\','."\n";
                $updateValidate.= '        if (isset($params[\''.$columnName.'\'])) {'."\n".'            $data[\''.$column['column_name'].'\'] = $params[\''.$columnName.'\'];'."\n".'        }'."\n";
                $listValidate.= '        if (isset($params[\''.$columnName.'\']) && $params[\''.$columnName.'\']) {'."\n".'            $where[] = [\''.$column['column_name'].'\',$params[\''.$columnName.'\']];'."\n".'        }'."\n";

            }
        }



        $stub = file_get_contents(__DIR__. '/stubs/Action.stub');
        $fileContent = str_replace(
            ['%NAME%', '%MODEL%', '%MODELX%','%ADDVALIDATE%','%UPDATEVALIDATE%','%LISTVALIDATE%'],
            [Str::studly($pluginName), Str::studly(Str::singular($table)),Str::lcfirst(Str::studly(Str::singular($table))),$addValidate,$updateValidate,$listValidate],
            $stub
        );

        if (! file_exists($path)) {
            $this->mkdir($path);
            file_put_contents($path, $fileContent);
        }


    }

    //createActionInterface
    protected function createIAction(string $pluginName, string $table): void
    {
        $path = BASE_PATH. DIRECTORY_SEPARATOR. 'plugins'.DIRECTORY_SEPARATOR.$this->pluginName. DIRECTORY_SEPARATOR.'src'. DIRECTORY_SEPARATOR.'Action'. DIRECTORY_SEPARATOR .'Interface'. DIRECTORY_SEPARATOR."I". Str::studly(Str::singular($table)). 'Action.php';
        $stub = file_get_contents(__DIR__. '/stubs/IAction.stub');

        $stub = str_replace(
            ['%NAME%', '%MODEL%', '%MODELX%'],
            [Str::studly($pluginName), Str::studly(Str::singular($table)),Str::lcfirst(Str::studly(Str::singular($table)))],
            $stub
        );
        if (! file_exists($path)) {
            $this->mkdir($path);
            file_put_contents($path, $stub);
        }

    }

    //createILogic
    protected function createILogic(string $pluginName, string $table): void
    {
        $path = BASE_PATH. DIRECTORY_SEPARATOR. 'plugins'.DIRECTORY_SEPARATOR.$this->pluginName. DIRECTORY_SEPARATOR.'src'. DIRECTORY_SEPARATOR.'Logic'. DIRECTORY_SEPARATOR.'Interface'. DIRECTORY_SEPARATOR."I". Str::studly(Str::singular($table)). 'Logic.php';
        $stub = file_get_contents(__DIR__. '/stubs/ILogic.stub');
        $stub = str_replace(
            ['%NAME%', '%MODEL%', '%MODELX%'],
            [Str::studly($pluginName), Str::studly(Str::singular($table)),Str::lcfirst(Str::studly(Str::singular($table)))],
            $stub
        );
        if (! file_exists($path)) {
            $this->mkdir($path);
            file_put_contents($path, $stub);
        }
    }
    //createLogic
    protected function createLogic(string $pluginName, string $table): void
    {
        $path = BASE_PATH. DIRECTORY_SEPARATOR. 'plugins'.DIRECTORY_SEPARATOR.$this->pluginName. DIRECTORY_SEPARATOR.'src'. DIRECTORY_SEPARATOR.'Logic'. DIRECTORY_SEPARATOR. Str::studly(Str::singular($table)). 'Logic.php';
        $stub = file_get_contents(__DIR__. '/stubs/Logic.stub');
        $stub = str_replace(
            ['%NAME%', '%MODEL%', '%MODELX%'],
            [Str::studly($pluginName), Str::studly(Str::singular($table)),Str::lcfirst(Str::studly(Str::singular($table)))],
            $stub
        );
        if (! file_exists($path)) {
            $this->mkdir($path);
            file_put_contents($path, $stub);
        }
    }
}
